package com.gzsxy.config;

import com.gzsxy.entity.TPermission;
import com.gzsxy.service.PermissionService;
import com.gzsxy.utils.Base64Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.List;

/**
 * @author xiaolong
 * @version 1.0
 * @description: 拦截器 Security权限认证
 * @date 2021/11/4 13:59
 */
@Component
@EnableWebSecurity //开启WebSecurity模式
public class SecurityAutoConfig extends WebSecurityConfigurerAdapter {

    /**
     * 用户认证类
     */
    @Autowired
    private MemberDetailsService memberDetailsService;

    @Autowired
    private PermissionService permissionService;

    /**
     * 密码加密类
     */
    @Autowired
    private PasswordEncoder passwordEncoder;


    /**
     * 认证  springboot 2.1.x可以直接使用
     * 密码编码：passwordEncoder
     * 在spring security 5.0+新增了很多的加密方法
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

//        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
//                .withUser("mayikt")

//                .password(new BCryptPasswordEncoder().encode("mayikt"))
//                .authorities("/*");

        //memberDetailsService类是最先请求先去执行的，通过查询数据库获取账户信息去验证
        auth.userDetailsService(memberDetailsService).passwordEncoder(new PasswordEncoder() {

            //rawPassword输入的密码
            @Override
            public String encode(CharSequence rawPassword) {
                return Base64Util.encryptToMD5((String)rawPassword);
            }


            //encodedPassword查询数据库里面已经经过md5加密后的密码
            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                String rawPass = Base64Util.encryptToMD5((String) rawPassword);
                boolean result = rawPass.equals(encodedPassword);
                return result;
            }
        });

    }

    /**
     * 授权
     * 链式编程
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

///**
// * 拦截 http 安全认证模式 设置为formLogin模式
// */
//        http.authorizeRequests().antMatchers("/**")
//                .fullyAuthenticated()
//                .antMatchers("/user").permitAll()
//                .and().formLogin();

//        http.authorizeRequests()
//                .anyRequest().authenticated() //所有请求都需要通过认证
//                .and()
//                .httpBasic() //Basic登录




//
        List<TPermission> allPermission =
                permissionService.findAllPermission();

        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry
                authorizeRequests = http.authorizeRequests();

        //添加所有权限
        allPermission.forEach((p) ->{
            authorizeRequests.antMatchers(p.getUrl()).hasAnyAuthority(p.getPermTag());
        });
        //表单提交给框架验证的路径
        http.formLogin().loginProcessingUrl("/Login");
        //是访问自己接口进行跳转页面的路径
        http.formLogin().loginPage("/toLogin");

//
////        //防止网站攻击：get post --新版本已经融合了
////        http.csrf().disable(); //关闭csrf功能
        http.csrf().disable();
    }




    /**
     //     * @description: 原因：升级为Security5.0以上密码支持多中加密方式，恢复以前模式
     //     * 如果在用户认证那BCryptPasswordEncoder不通过这个加密，权限通不过，
     //     * 如果不加BCryptPasswordEncoder，可以通过添加以下方式也可以实现认证
     //     */
//    @Bean
//    public static NoOpPasswordEncoder passwordEncoder(){
//        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
//    }





    /**
     * 解决AuthenticationManager类无法注入问题  密码模式
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }


    ////设置授权码模式的授权码如何 存取，这里采用内存方式
//    @Bean
//    public AuthorizationCodeServices authorizationCodeServices() {
//        return new InMemoryAuthorizationCodeServices();
//    }

    /**
     * 设置授权码模式的授权码如何 存取，这里采用数据库
     */
    @Bean
    public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource){
        return new JdbcAuthorizationCodeServices(dataSource);
    }



    /**
     * 采用查询数据库方式获取客户端信息  clientDetailsServices Bean名字设置不同否则会覆盖 注入使用这个名字
     */
    @Bean("clientDetailsServices")
    public ClientDetailsService clientDetailsService(DataSource dataSource){
        JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
        ((JdbcClientDetailsService)clientDetailsService).setPasswordEncoder(passwordEncoder);
        return clientDetailsService;
    }


}
